
//#include "stdbool.h"
#include <js_native_api.h>
#include "../include/WorkDemo.h"
#include <js_native_api_types.h>
#include <string>
#include <node_api.h>
#include <hilog/log.h>
#include "math.h"
#include "uv.h"

#define LOG_DOMAIN 0
#define LOG_TAG "demoTest"

/*
 * JS侧回调C++方法
 * @param env 方法执行上下文，相当于JS的this/context对象
 * @param info 方法回调参数
 * @return JS方法返回值
 */
napi_value WorkDemo::Add(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "add");
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);

    return sum;
}

napi_value WorkDemo::Sub(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "Sub");
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value sum;
    napi_create_double(env, value0 - value1, &sum);

    return sum;
}

napi_value WorkDemo::hyPotC(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LogType::LOG_APP, "hyPotC");
    // 参数数量
    size_t argc = 2;
    // 声明参数数组
    napi_value args[2] = {nullptr};
    // 获取传入的参数并依次放入参数数组中
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    // 对第一个js参数类型进行判定
    napi_valuetype valueType0;
    napi_typeof(env, args[0], &valueType0);
    // 对第二个js参数类型进行判定
    napi_valuetype valueType1;
    napi_typeof(env, args[1], &valueType1);
    // 将第一个传入参数转化为double类型
    double value0;
    napi_get_value_double(env, args[0], &value0);
    // 将第二个传入参数转化为double类型
    double value1;
    napi_get_value_double(env, args[1], &value1);
    // 声明返回结果字段，并调用C标准库的hypot方法进行计算
    napi_value sum;
    double result = hypot(value0, value1);
    napi_create_double(env, result, &sum);
    return sum;
}

napi_value WorkDemo::addSync(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "addSync");
    size_t requireArgc = 3;
    size_t argc = 3;
    napi_value args[3] = {nullptr};
    napi_value context = nullptr;

    napi_get_cb_info(env, info, &argc, args, &context, nullptr);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value callbackArg[1] = {nullptr};

    napi_create_double(env, value0 + value1, &callbackArg[0]);

    napi_value res = nullptr;
    napi_call_function(env, context, args[2], 1, callbackArg, &res);

    return res;
}

napi_value WorkDemo::addAsyncByPromise(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "addAsync");
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    SyncParam *param = new SyncParam();
    napi_get_value_double(env, args[0], &(param->num1));
    napi_get_value_double(env, args[1], &(param->num2));

    napi_value resource = nullptr;
    std::string funcName = "addAsyncByPromise";
    napi_create_string_utf8(env, "addAsyncByPromise", funcName.length(), &resource);
    //创建异步任务
    napi_create_async_work(
        env, nullptr, resource,
        [](napi_env env, void *data) { //异步任务执行回调函数
            OH_LOG_INFO(LOG_APP, "trigger execute data");
            SyncParam *param = (SyncParam *)data;
            //OH_LOG_INFO(LOG_APP, "add promise param is %{public}f, %{public}f", param->num1, param->num2);
            param->result = param->num1 + param->num2;
        },
        [](napi_env env, napi_status status, void *data) { //异步任务执行结束后，结果回调
            SyncParam *param = (SyncParam *)data;
            if (param == nullptr) {
                OH_LOG_ERROR(LOG_APP, "param is nullptr");
                return;
            }
            OH_LOG_INFO(LOG_APP, "add promise param->result is %{public}f", param->result);
            napi_value target = nullptr;
            napi_create_double(env, param->result, &target);
            //promise resolve
            napi_resolve_deferred(env, param->deferred, target);

            //promise reject
            //待补充

            //删除异步任务
            napi_delete_async_work(env, param->worker);
            delete param;
            param = nullptr;
        },
        param, &param->worker);

    //create async work
    napi_queue_async_work(env, param->worker);

    //promise value for this function
    napi_value result = nullptr;
    napi_create_promise(env, &param->deferred, &result);

    return result;
}

napi_value WorkDemo::getAsyncByCallback(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "getAsyncByCallback");
    size_t argc = 3;
    napi_value args[3];
    napi_value context;
    void *data = nullptr;

    napi_get_cb_info(env, info, &argc, args, &context, nullptr);

    auto asyncContext = new SyncParamAsync();
    asyncContext->thisVar = context;
    napi_get_value_double(env, args[0], &(asyncContext->num1));
    napi_get_value_double(env, args[1], &(asyncContext->num2));

    napi_create_reference(env, args[2], 1, &asyncContext->callbackRef);

    napi_value target = nullptr;
    napi_create_double(env, asyncContext->num1 + asyncContext->num2, &target);

    napi_value resource = nullptr;
    std::string funcName = "getAsyncByCallback";
    napi_create_string_utf8(env, "getAsyncByCallback", funcName.length(), &resource);

    napi_create_async_work(
        env, nullptr, resource,
        [](napi_env env, void *data) {
            OH_LOG_INFO(LOG_APP, "Test NAPI start async work on c");
        },
        [](napi_env env, napi_status status, void *data) {
            napi_status varStatus;
            SyncParamAsync *innerAsyncContext = (SyncParamAsync *)data;

            napi_value callbackArg[1] = {nullptr};

            napi_create_double(env, 1234, &callbackArg[0]);

            napi_value callback = nullptr;
            varStatus = napi_get_reference_value(env, innerAsyncContext->callbackRef, &callback);
            OH_LOG_INFO(LOG_APP, "get reference value varStatus:%{public}d", varStatus == napi_ok ? 1 : 0);

            napi_value result;
            varStatus = napi_call_function(env, innerAsyncContext->thisVar, callback, 1, callbackArg, &result);
            OH_LOG_INFO(LOG_APP, "call function varStatus:%{public}d napi_ok?= %{public}d", varStatus, varStatus == napi_ok ? 1 : 0);

            if (varStatus != napi_ok) {
                const napi_extended_error_info *info = nullptr;
                napi_get_last_error_info(env, &info);
            }

            napi_delete_reference(env, innerAsyncContext->callbackRef);

            napi_delete_async_work(env, innerAsyncContext->work);
            delete innerAsyncContext;
        },
        (void *)asyncContext,
        &asyncContext->work);

    napi_queue_async_work(env, asyncContext->work);

    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

//调用的方法在这里
//https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-display.md#displaygetdefaultdisplaysync9
//display.getDefaultDisplay
/*
   let displayClass = null;
   let promise = display.getDefaultDisplay();
   promise.then((data) => {
       displayClass = data;
       console.info('Succeeded in obtaining the default display object. Data:' + JSON.stringify(data));
   }).catch((err) => {
       console.error('Failed to obtain the default display object. Code:  ' + JSON.stringify(err));
   });

 */
napi_value WorkDemo::CallPromiseMethod(napi_env env, napi_callback_info info) {
    const uint8_t argsNum = 3;
    OH_LOG_INFO(LOG_APP, "call js promise method");
    // for example call js method display.getDefaultDisplay().then((val)=>{})
    napi_value global = nullptr;
    napi_get_global(env, &global);

    // load disply module
    napi_value requireFunc = nullptr;
    napi_get_named_property(env, global, "requireNapi", &requireFunc);

    //获取napi能力模块的display module
    std::string moduleName = "display_napi";
    napi_value args[1] = {nullptr};
    napi_create_string_utf8(env, moduleName.c_str(), moduleName.size(), &args[0]);
    napi_value requireValue = nullptr;
    napi_call_function(env, global, requireFunc, 1, &args[0], &requireValue);

    //调用display模块上方法getDefaultDisplay
    napi_value displayFunc = nullptr;
    napi_get_named_property(env, requireValue, "getDefaultDisplay", &displayFunc);
    napi_value displayPromise = nullptr;
    napi_call_function(env, requireValue, displayFunc, 0, nullptr, &displayPromise);

    auto success = [](napi_env env, napi_callback_info info) -> napi_value {
        OH_LOG_INFO(LOG_APP, "trigger promise function");
        napi_value result = nullptr;
        const size_t argsNum = 1;
        napi_value args[argsNum] = {nullptr};
        size_t argNum = 1;
        napi_get_cb_info(env, info, &argNum, args, nullptr, nullptr);
        OH_LOG_INFO(LOG_APP, "target app args is %{public}zu", argsNum);
        napi_valuetype type;
        napi_typeof(env, args[0], &type);
        if (type != napi_valuetype::napi_object) {
            OH_LOG_INFO(LOG_APP, "get target value type failed %{public}d, target type is %{public}d", type, napi_valuetype::napi_object);
        }
        napi_value widthProp = nullptr;
        napi_get_named_property(env, args[0], "height", &widthProp);
        double width = 0;
        napi_get_value_double(env, widthProp, &width);
        OH_LOG_INFO(LOG_APP, "size is %{public}f", width);
        napi_value res = nullptr;
        napi_create_double(env, width, &res);
        return res;
    };

    //获取promise的then， 把success传入进行调用
    napi_value displayPromiseFunc = nullptr;
    napi_get_named_property(env, displayPromise, "then", &displayPromiseFunc);
    napi_value sizeFunc = nullptr;
    napi_create_function(env, "getSize", 1, success, nullptr, &sizeFunc);
    napi_value res = nullptr;
    napi_value displayParam[1] = {sizeFunc};

    //触发then方法
    napi_call_function(env, displayPromise, displayPromiseFunc, 1, &displayParam[0], &res);
    return res;
}

//toutou
napi_value WorkDemo::CallPromiseMethodSync(napi_env env, napi_callback_info info) {
    size_t argsNum = 1;
    napi_value args[1] = {nullptr};
    napi_value context = nullptr;
    OH_LOG_INFO(LOG_APP, "huangzhiquan CallPromiseMethodSync");
    napi_get_cb_info(env, info, &argsNum, args, &context, nullptr);
    bool isPromise = false;
    napi_get_value_bool(env, args[0], &isPromise);
    char *outparam = "init";
    WorkParam *param = new (std::nothrow) WorkParam{env, "inParam", outparam, std::make_shared<ThreadInfo>()};
    if (isPromise) {
        std::thread threadTestPromise(WorkDemo::RunPromiseBySync, param);
        threadTestPromise.detach();
    } else {
        std::thread threadTestCallback(RunCallbackMethod, param);
        threadTestCallback.detach();
    }
    napi_value res = nullptr;
    napi_get_undefined(env, &res);
    return res;
}

//bei toutou diaoyong
void WorkDemo::RunPromiseBySync(WorkParam *param) {
    OH_LOG_INFO(LOG_APP, "start to run promise by sync");
    uv_loop_s *loop = nullptr;

    // get js loop for this method
    napi_get_uv_event_loop(param->env, &loop);

    uv_work_t *work = new (std::nothrow) uv_work_t;
    if (work == nullptr) {
        OH_LOG_INFO(LOG_APP, "failed to new uv_work_t");
        return;
    }

    work->data = param;

    uv_queue_work(
        loop, work, [](uv_work_t *work) {},
        [](uv_work_t *work, int status) 
    {
       // do js task
       WorkParam *param = reinterpret_cast<WorkParam *>(work->data);
       if (param == nullptr) {
           OH_LOG_INFO(LOG_APP, "target work param is nullptr");
           return;
       }
       OH_LOG_INFO(LOG_APP, "start to get work param");
       napi_value global = nullptr;
       napi_get_global(param->env, &global);
       napi_value globalThis = nullptr;
       napi_get_named_property(param->env, global, "globalThis", &globalThis);
       napi_value targetFunc = nullptr;
       napi_get_named_property(param->env, global, "testPromiseFunc", &targetFunc);
       // get promise object
       napi_value promiseObj = nullptr;
       napi_call_function(param->env, globalThis, targetFunc, 0, nullptr, &promiseObj);
       // get then func
       napi_value promiseThen = nullptr;
       napi_get_named_property(param->env, promiseObj, "then", &promiseThen);
       auto successCallback = [](napi_env env, napi_callback_info info) -> napi_value {
           size_t argc = 1;
           napi_value args[1] = { nullptr };
           void *param_in = nullptr;
           napi_get_cb_info(env, info, &argc, args, nullptr, &param_in);
           WorkParam *param = reinterpret_cast<WorkParam *>(param_in);
           if (param == nullptr) {
               OH_LOG_INFO(LOG_APP, "param is nullptr");
               napi_value res = nullptr;
               return nullptr;
           }
           char retChar[255];
           size_t retLen = 0;
           napi_get_value_string_utf8(env, args[0], retChar, 255, &retLen);
           param->outParam = retChar;
           OH_LOG_INFO(LOG_APP, "result is %{public}s", param->outParam);
           std::unique_lock<std::mutex> lock(param->lockInfo->mutex);
           param->lockInfo->ready = true;
           param->lockInfo->condition.notify_all();
           return nullptr;
       };
       napi_value successFunc = nullptr;
       napi_create_function(param->env, "successFunc", NAPI_AUTO_LENGTH, successCallback, param, &successFunc);
       napi_value ret = nullptr;
       napi_call_function(param->env, promiseObj, promiseThen, 1, &successFunc, &ret);
       std::unique_lock<std::mutex> lock(param->lockInfo->mutex);
       param->lockInfo->condition.wait(lock, [&param] {return param->lockInfo->ready;});
       delete work;
       work = nullptr;
       delete param;
       param = nullptr;
    });
}

////bei toutou diaoyong
void WorkDemo::RunCallbackMethod(WorkParam *param) {
    OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step0");
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(param->env, &loop);
    uv_work_t *work = new (std::nothrow) uv_work_t;
    if (work == nullptr) {
        OH_LOG_INFO(LOG_APP, "create work failed");
        delete param;
        param = nullptr;
    }
    work->data = (void*)param;
    
    OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step1");
    uv_queue_work(loop, work, [](uv_work_t *work) {}, [](uv_work_t *work, int status) {
        WorkParam *param = (WorkParam *)(work->data);
        if (param == nullptr) {
            OH_LOG_INFO(LOG_APP, "param is nullptr");
            return;
        }
        
        OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step2");
        
        napi_value global = nullptr;
        napi_get_global(param->env, &global);
        napi_value globalThis = nullptr;
        napi_get_named_property(param->env, global, "globalThis", &globalThis);
        // 获取注册的testCallbackFunc
        napi_value testCallbackFunc = nullptr;
        napi_get_named_property(param->env, globalThis, "testCallbackFunc", &testCallbackFunc);
        
        OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step3");
        
        // 创建callback回传js方法入参
        auto cb = [](napi_env env, napi_callback_info info) -> napi_value {
            OH_LOG_INFO(LOG_APP, "trigger callback success");
           
            size_t argc_in = 1;
            napi_value args[1] = {nullptr};
            void *workParam = nullptr;
            napi_get_cb_info(env, info, &argc_in, args, nullptr, &workParam);
            WorkParam *callbackParam = (WorkParam *)workParam;
            
            OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step4");
            if (callbackParam = nullptr) {
                OH_LOG_INFO(LOG_APP, "callback param is nullptr");
                napi_value result = nullptr;
                napi_get_undefined(env, &result);
                return result;
            }
            OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step5");
            const size_t retSize = 255;
            char retChar[retSize] = {0};
            size_t targetSize = 255;

            OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step5_1");
            napi_get_value_string_utf8(env, args[0], retChar, 255, &targetSize);
            OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step5_2");

            OH_LOG_INFO(LOG_APP, "callback args is %{public}s", retChar);
             /* 没明白为什么会挂
            callbackParam->outParam = retChar;

            OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step6");
                                              
            std::unique_lock<std::mutex> lock(callbackParam->lockInfo->mutex);
            callbackParam->lockInfo->ready = true;
            OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step7");
            callbackParam->lockInfo->condition.notify_all();

    */
            return nullptr;
        };
        OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step8");
        napi_value successArgs[1] = {nullptr};
        size_t argsNum = 1;
        OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step9");
        napi_create_function(param->env, "successFunc", NAPI_AUTO_LENGTH, cb, param, &successArgs[0]);
        napi_value ret = nullptr;
        napi_call_function(param->env, globalThis, testCallbackFunc, argsNum, successArgs, &ret); 
    });
    OH_LOG_INFO(LOG_APP, "huangzhiquan run callback method step10");
    std::unique_lock<std::mutex> lock(param->lockInfo->mutex);
    param->lockInfo->condition.wait(lock, [&param] { return param->lockInfo->ready; });
    delete param;
    param = nullptr;
}
